package com.leenmvc.core.utils.thread.runnables;

import com.github.pagehelper.Page;
import com.leenmvc.core.annotation.DbInfo;
import com.leenmvc.core.base.BaseEntity;
import com.leenmvc.core.base.EntityInfo;
import com.leenmvc.core.dao.communal.BaseDao;
import com.leenmvc.core.dao.mysql.MysqlBaseDaoImpl;
import com.leenmvc.core.dao.mysql.wrapper.QueryWrapper;
import com.leenmvc.core.security.entity.core.SysApi;
import com.leenmvc.core.security.entity.core.SysApiParam;
import com.leenmvc.core.security.entity.core.SysApiResult;
import com.leenmvc.core.security.entity.schema.Columns;
import com.leenmvc.core.utils.ClassUtil;
import com.leenmvc.core.utils.StringUtils;
import com.leenmvc.core.utils.collection.ArrayUtils;
import com.leenmvc.core.utils.unique.SnowFlake;
import com.leenmvc.core.base.result.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;


public class BuildApi implements Runnable {

    private final static Logger logger = LoggerFactory.getLogger(BuildApi.class);

    private BaseDao baseDao = MysqlBaseDaoImpl.getInstance();

    /**
     * 将要被添加的操作日志
     */
    private static Queue<SysApi> apiQueue = new LinkedBlockingQueue<SysApi>();

    @Override
    public void run() {
        Thread.currentThread().setName("thread-buildApi");
        while (true) {
            try {
                for (int i = 0; i < apiQueue.size(); i++) {
                    SysApi api = apiQueue.poll();
                    SysApi existApi = baseDao.get(SysApi.class, "uri", api.getUri(), "operation", api.getOperation(), "requestMethod", api.getRequestMethod());
                    boolean addApi = true;
                    if (existApi == null) {
                        api.setId(SnowFlake.generateId());
                        addApi = baseDao.insert(api);
                    } else {
                        api.setId(existApi.getId());
                        if (existApi.getProtecting() != null && existApi.getProtecting() == 1) {
                            continue;
                        }
                    }

                    if (addApi) {
                        // 建构参数列表
                        buildParams(api);

                        // 存结果
                        Result requestResult = api.getRequestResult();
                        if (null != requestResult) {
                            logger.info("删除了【{}】条ApiResult记录", baseDao.delete(SysApiResult.class, "apiId", api.getId()));
                            buildResult(requestResult, api);
                        }
                    }
                }

                Thread.sleep(50);
            } catch (InterruptedException e) {
                logger.error(e.getMessage(), e);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    /**
     * API中的参数部分构建方法
     *
     * @param api
     */
    private void buildParams(SysApi api) {
        baseDao.delete(SysApiParam.class, "apiId", api.getId());
        Object requestBody = api.getRequestBody();
        if (!StringUtils.isEmpty(requestBody)) { // 不是body就是 request
            handlerParamForBody(api, requestBody);
        } else {
            handlerParamForForm(api);
        }
    }

    /**
     * 用来处理除了requestBody类型之外的的参数
     * Form表单，get请求等
     *
     * @param api
     */
    private void handlerParamForForm(SysApi api) {
        String requestParams = api.getRequestParams();
        String[] overParam = new String[]{"_t", "orderField", "order", "null"};
        if (requestParams != null) {
            String[] paramKV = requestParams.split("&");
            a:
            for (int j = 0; j < paramKV.length; j++) {
                String[] key = paramKV[j].split("=");
                SysApiParam o = baseDao.get(SysApiParam.class, "apiId", api.getId(), "paramName", key[0]);
                if (o == null) {
                    b:
                    for (String over : overParam) {
                        if (over.equals(key[0])) {
                            continue a;
                        }
                    }
                    SysApiParam sysApiParam = new SysApiParam();
                    sysApiParam.setId(SnowFlake.generateId());
                    sysApiParam.setApiId(api.getId());
                    sysApiParam.setParamName(key[0]);
                    sysApiParam.setParamDataType("字符串");
                    sysApiParam.setParamType("表单类型");
                    sysApiParam.setRequired(1);
                    String defaultResult = this.defaultResult.get(sysApiParam.getParamName());
                    sysApiParam.setNote(defaultResult == null ? "请补充": defaultResult);
                    baseDao.insert(sysApiParam);
                }
            }
        }
    }

    private void handlerParamForBody(SysApi api, Object requestBody) {
        Class<?> cls = requestBody.getClass();
        String bodyType = cls.getName();
        switch (bodyType) {
            case "java.lang.String":
                if (!"DELETE".equals(api.getRequestMethod())) break;
                SysApiParam o = baseDao.get(SysApiParam.class, "apiId", api.getId(), "paramName", "ids");
                if (o == null) {
                    SysApiParam sysApiParam = new SysApiParam();
                    sysApiParam.setId(SnowFlake.generateId());
                    sysApiParam.setApiId(api.getId());
                    sysApiParam.setParamName("ids");
                    sysApiParam.setParamDataType("数值数组");
                    sysApiParam.setNote("将要删除数据的ID列表");
                    sysApiParam.setParamType("requestBody");
                    baseDao.insert(sysApiParam);
                }
                break;
            default:
                Field[] declaredFields = cls.getDeclaredFields();
                for (int j = 0; j < declaredFields.length; j++) {
                    o = baseDao.get(SysApiParam.class, "apiId", api.getId(), "paramName", declaredFields[j].getName());
                    if (o == null) {
                        SysApiParam sysApiParam = new SysApiParam();
                        sysApiParam.setId(SnowFlake.generateId());
                        sysApiParam.setApiId(api.getId());
                        sysApiParam.setParamName(declaredFields[j].getName());
                        sysApiParam.setParamDataType(declaredFields[j].getType().getTypeName());
                        QueryWrapper wrapper = BaseEntity.getDialect(Columns.class).getWrapperFactory().getQueryWrapper(Columns.class);
                        wrapper.incField("columnComment")
                                .eq("tableSchema", "db_financial_dev")
                                .eq("tableName", BaseEntity.getTableName(cls)).eq("columnName", declaredFields[j].getName());
                        Object oneField = baseDao.getOneField(wrapper);
                        sysApiParam.setNote(String.valueOf(oneField));
                        sysApiParam.setParamType("requestBody");
                        baseDao.insert(sysApiParam);
                    }
                }
        }
    }

    private Class[] clss = new Class[]{BigDecimal.class, Integer.class, Float.class, Double.class, Long.class, Short.class, String.class, Byte.class, Character.class, Boolean.class, Timestamp.class, Date.class};

    private Map<String, String> defaultResult = new HashMap<String, String>() {{
        put("Result", "顶级结果句柄");
        put("code", "结果码（0-成功返回、250-提示信息、500-异常、401-权限问题）");
        put("page", "第几页");
        put("pageSize", "页大小");
        put("msg", "提示信息");
        put("data", "正式数据");
        put("total", "总记录数");
        put("pages", "总页数");
    }};

    private Long pid = 0l;
    private SysApiResult sysApiResult = new SysApiResult();

    /**
     * @param t
     * @param api
     * @param <T>
     */
    private <T> void buildResult(T t, SysApi api) {
        Class<?> cls = t.getClass();
        if (ArrayUtils.contain(clss, t.getClass())) {

        } else if (cls.equals(Page.class)) {
            Page t1 = (Page) t;
            Iterator iterator = t1.iterator();
            if(iterator.hasNext()) {
                Object next = iterator.next();
                Class<?> pageClass = next.getClass();
                buildSingle(next, api, pageClass);
            }

        } else if ("java.util.ArrayList".equals(t.getClass().getName())) {
            ArrayList t1 = (ArrayList) t;
            pid = sysApiResult.getId();
            if (t1.size() > 0) {
                buildResult(t1.get(0), api);
            }
        } else if ("java.util.HashMap".equals(t.getClass().getName())) {
            Map t1 = (HashMap) t;
            pid = sysApiResult.getId();
            sysApiResult.setPid(pid);
            buildSingle(t1, api, t1.getClass());
            pid = sysApiResult.getId();
            for (Object key : t1.keySet()) {
                Object o = t1.get(key);
                buildMapKey(key, o, api, o.getClass());
            }
        } else {
            buildSingle(t, api, cls);
        }
    }

    private <K,V> void buildMapKey(Object key, Object value, SysApi api, Class<?> cls) {
        sysApiResult.setId(SnowFlake.generateId());
        sysApiResult.setApiId(api.getId());
        sysApiResult.setNote(String.valueOf(value));
        sysApiResult.setParamName(String.valueOf(key));
        sysApiResult.setParamType(value.getClass().getSimpleName());
        sysApiResult.setPid(pid);
        if (!baseDao.insert(sysApiResult)) {
            logger.info("插入API结果时发生错误！！请求为：{}", sysApiResult.getNote());
        };

    }

    private <T> void buildSingle(T t, SysApi api, Class<?> cls) {
        sysApiResult.setId(SnowFlake.generateId());
        sysApiResult.setApiId(api.getId());
        EntityInfo entity = BaseEntity.entityMap.get(StringUtils.lowercaseFirstChar(cls.getSimpleName()));
        if (entity != null) {
            DbInfo dbInfo = cls.getAnnotation(DbInfo.class);
            sysApiResult.setParamName(cls.getSimpleName());
            sysApiResult.setParamType("实体");
            sysApiResult.setNote(dbInfo.comment());
        } else {
            sysApiResult.setParamName(cls.getSimpleName());
            sysApiResult.setParamType(cls.getTypeName());
            sysApiResult.setNote(defaultResult.get(cls.getSimpleName()));
        }

        if (!baseDao.insert(sysApiResult)) {
            logger.info("插入API结果时发生错误！！请求为：{}", sysApiResult.getNote());
        };
        BuildEntityResult(t, api, cls);
    }

    private <T> void BuildEntityResult(T t, SysApi api, Class<?> cls) {
        pid = sysApiResult.getId();
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            try {
                if (field.getModifiers() == 2) {
                    sysApiResult.setId(SnowFlake.generateId());
                    sysApiResult.setApiId(api.getId());
                    sysApiResult.setPid(pid);
                    sysApiResult.setParamType(field.getType().getName());
                    sysApiResult.setParamName(field.getName());
                    if (BaseEntity.classInfo.get(cls) != null) {
                        QueryWrapper wrapper = BaseEntity.getDialect(Columns.class).getWrapperFactory().getQueryWrapper(Columns.class);
                        wrapper.incField("columnComment", "characterMaximumLength")
                                .eq("tableSchema", "db_leenmvc_prod")
                                .eq("tableName", BaseEntity.getTableName(cls)).eq("columnName", BaseEntity.humpLine(field));
                        Columns columnInfo = baseDao.get(wrapper);
                        if (columnInfo != null) {
                            sysApiResult.setNote(columnInfo.getColumnComment());
                            sysApiResult.setLen(columnInfo.getCharacterMaximumLength());
                        }
                    } else {
                        String paramName = sysApiResult.getParamName();
                        sysApiResult.setNote(defaultResult.get(paramName));
                        sysApiResult.setLen(0l);
                    }

                    Object fieldValue = ClassUtil.getFieldValue(t, field.getName());
                    if (fieldValue == null) continue;
//                    sysApiResult.setExample(fieldValue.toString());
                    if (!baseDao.insert(sysApiResult)) {
                        logger.info("插入API结果时发生错误！！请求为：{}", sysApiResult.getNote());
                    }
                    ;
                    buildResult(fieldValue, api);
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }

        }
    }

    public static void addApi(SysApi log) {
        apiQueue.add(log);
    }

}
